<?php
// +----------------------------------------------------------------------
// | OneThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.onethink.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------

namespace Wechat\Api;

use Wechat\Api\Api;
use Wechat\Model\TchatClientModel;

class WechatApi extends Api {
    //TODO 将字符串替换为空，并在初始化方法中读取配置信息赋值
    private $encodingAesKey = '';
    private $token = '';
    private $data = array();
    private $appId = '';
    private $timeStamp = '';
    private $nonce = '';

    /**
     * 构造方法，实例化操作模型
     */
    protected function _init() {
        $config = get_wechat_config();
        $this->appId = $config['WECHAT_APP_ID'];
        $this->encodingAesKey = $config['WECHAT_EncodingAESKey'];
        $this->token = $config['WECHAT_TOKEN'];
        $this->timeStamp = NOW_TIME;
        $this->nonce = get_rand_chars();
    }

     /*
     * 消息回应方法
     * 微信后台URL设置CONTROLLER方法，检测是否是验证，非验证则进行消息回应
     */
    public function reply() {

       if (isset($_GET['echostr'])) {//接入验证
            $this->valid();
        } else {
            //如果不是接入验证，则进入客户消息响应流程
            $this->responseMsg();
        }
    }

    /**
     * 响应用户消息
     */
    private function responseMsg() {
        /* 获取服务器发送过来的数据，并赋值给解密和加密用到的变量 */
        $msgSignature = $_GET['msg_signature'];
        $timestamp = $_GET['timestamp'];
        $nonce = $_GET['nonce'];
        $xml = file_get_contents('php://input');

        //载入微信消息加密解密第三方类库
        Vendor('WXBiz.wxBizMsgCrypt');
        $pc = new \WXBizMsgCrypt($this->token, $this->encodingAesKey, $this->appId);

        /* 获取加密消息，并解密出来 */
        $msg='';
        $errCode = $pc->decryptMsg($msgSignature, $timestamp, $nonce, $xml, $msg);
        if ($errCode == 0) {
            //解密成功
            $postObj = simplexml_load_string($msg , 'SimpleXMLElement' , LIBXML_NOCDATA);
            //将客户消息转换为数据数组
            foreach ( $postObj as $key => $value ) {
                $this->data[$key] = strval($value);
            }

            /* 获取要回复给客户的消息内容和类型 */
            //用list函数将获取回来的数组赋值给$content和$type
            //$content 要回复的内容 数据类型为string或array()
            //$type 要回复的格式 text/news/video/music 等等
            $data = $this->data;
            list( $content , $type) = $this->getReply($data);

            //整理并获取响应给当前客户的明文信息
            $replay = $this->response($content , $type);
            //加密信息并回复给客户
            $encryptMsg = '';
            $errCode = $pc->encryptMsg($replay,$this->timeStamp,$this->nonce,$encryptMsg);
            if($errCode == 0){
                print ($encryptMsg);
            }else{
                print($errCode . "\n");
            }

        } else {
            print($errCode . "\n");
        }

    }

    /**
     * 判断客户消息类型方法
     * 根据用户发送内容生成的数组判断其消息类型并分别获取不同回复数据
     *
     * @param array $data 经解析后的用户数据
     *
     * @return array 最终生成的回复内容数组
     * Tchat 待求证：据网络客户分析，如果开发者模式和授权第三方同时存在，微信会分别发送给两者一次，如果一方不能回复内容，会提示“该公众号暂时无法提供服务，请稍后再试” location类型不会，其他会。
     * 参考：https://blog.csdn.net/maobois/article/details/73505992 中的回复内容
     */
    private function getReply($data) {

        $openId = $data['FromUserName'];

        switch($data['MsgType']){
            case'text':
                $TextRe = A('Wechat/Text' , 'Event');
                $keyword = $data['Content'];
                $reply = $TextRe->textHandle($openId , $keyword);
                break;
            case 'event':
                $EventRe = A('Wechat/Event' , 'Event');
                $event = $data['Event'];
                $eventData = $data;
                $reply = $EventRe->eventHandle($openId , $event ,$eventData);
                break;
            case 'image':
                $ImageRe = A('Wechat/Image' , 'Event');
                $mediaId = $data['MediaId'];
                $reply = $ImageRe->imageHandle($openId , $mediaId);
                break;
            case 'location':
                $LocationRe = A('Wechat/Location' , 'Event');
                $location = array(
                    'Location_X'=>$data['Location_X'],
                    'Location_Y'=>$data['Location_Y'],
                    'Scale'=>$data['Scale'],
                    'Label'=>$data['Label']
                );
                $reply = $LocationRe->locationHandle($openId , $location);
                break;
            case 'voice':
                $VoiceRe = A('Wechat/Voice' , 'Event');
                $mediaId = $data['MediaId'];
                $reply = $VoiceRe->voiceHandle($openId , $mediaId);
                break;
            default:

                $reply = array('您的消息已成功发送过来，我们暂时还未能识别您的消息类型，我们后期会对此进行处理，感谢您的支持。', 'text' , 0 );
        }

        return $reply;
    }


    /**
     * 接收回复信息内容数组并整理后回复给用户
     *
     * @param  array  $content 回复信息的内容，文本、音乐信息为字符串、图文信息为数组
     * @param  string $type    消息类型，text news music
     *
     * @author 麦当苗儿 <zuojiazi@vip.qq.com>（有改动）
     * @return string          XML字符串
     */
    private function response($content , $type = 'text') {
        /* 基础数据 */
        $this->data = array(
            'ToUserName' => $this->data['FromUserName'] ,
            'FromUserName' => $this->data['ToUserName'] ,
            'CreateTime' => NOW_TIME ,
            'MsgType' => $type,
            'MsgId' => $this->data['MsgId']
        );

        /* 根据回复信息类型添加相应数据到回复数据中 */
        switch($type){
            case'text':
                $this->text($content);
                break;
            case'news':
                $this->news($content);
                break;
            case'image':
                $this->image($content);
                break;
            case'voice':
                $this->voice($content);
                break;
            case'video':
                $this->video($content);
                break;
            case'music':
                $this->music($content);
                break;

        }

        /*设置回复信息类型的模板 */
        $tpl = get_wechat_tpl($type);
        $data = $this->data;
        //转换数据为XML
        return $this->data2xml($type , $tpl , $data);
    }

    /**
     * 回复的字符串信息写入变量data
     * @author 麦当苗儿 <zuojiazi@vip.qq.com>
     *
     * @param  string $content 要回复的信息
     */
    private function text($content) {

        $this->data['ContentStr'] = $content;
    }

    /**
     * 回复的图片信息写入变量data
     *
     * @param  string $image 系统中存储的图片ID
     */
    private function image($image) {
        //将图片上传到微信服务器

        $this->data['MediaId'] = $image;
    }

    /**
     * 回复的voice信息写入变量data
     *
     * @param  string $voice 系统中存储的voice文件ID
     */
    private function voice($voice) {
        //将图片上传到微信服务器

        $this->data['MediaId'] = $voice;
    }

    /**
     * 回复的voice信息写入变量data
     *
     * @param  string $video 系统中存储的voice文件ID
     */
    private function video($arr) {
        //将图片上传到微信服务器
        list(
            $video['path'] ,
            $video['Title'] ,
            $video['Description'] ,
            ) = $arr;
        $mediaId = upload_wechat_media($video['path'],'video');
        $this->data['MediaId'] = $mediaId;
    }

    /**
     * 回复的音乐信息写入变量data
     *
     * @param  string $content 要回复的音乐
     */
    private function music($arr) {

        list(
            $music['Title'] ,
            $music['Description'] ,
            $music['MusicUrl'] ,
            $music['HQMusicUrl'],
            $music['thumb']
            ) = $arr;
        $music['ThumbMediaId'] = upload_wechat_media($music['thumb'],'thumb');
        $this->data['Music'] = $music;
    }

    /**
     * 回复的图文信息写入变量data
     * 对于多于10条记录的，进行缓存分页，并设定查看下一页的回复关键字
     *
     * @param  string $news 要回复的图文内容
     */
    private function news($news) {

        $openid = $this->data['ToUserName'];
        S($openid , NULL);
        $i = 0;

        foreach ( $news as $key => $var ) {
            $articles[$i]['Title'] =$var['title'];
            if ( $i == 0 ) {//如果是第一条，在没有指定封面的情况下使用默认图片，并加上描述

                $articles[$i]['Description'] = $var['description'];//描述会在单图文时显示，多条时不会显示所以只在第一条中设置。

                $defaultPicUrl = 'http://' . $_SERVER['HTTP_HOST'] . __ROOT__ . '/Public/Home/images/tchat/big/' . rand(1 , 29) . '.jpg';
                $articles[$i]['PicUrl'] = $var['cover_id'] == 0 ? $defaultPicUrl
                    :
                    'http://' . $_SERVER['HTTP_HOST'] . __ROOT__ . M('Picture')->where(array( 'id' => $var['cover_id'] ))->getField('path');

            } else {//其他条目如果没有指定封面，则设置为空
                $articles[$i]['PicUrl'] = $var['index_pic'] == 0 ? ''
                    :
                    'http://' . $_SERVER['HTTP_HOST'] . __ROOT__ . M('Picture')->where(array( 'id' => $var['index_pic'] ))->getField('path');
            }

            $modelName = $var['modelName'];

            $articles[$i]['Url'] = 'http://' . $_SERVER['HTTP_HOST'] . __ROOT__ . '/index.php?s=/Home/' . $modelName . '/detail/id/' . $var['id'] . '.html';
            $i++;
            unset( $news[$key] );

            if ( $i == 7) { //根据微信接口规则，跳转图文消息最多只允许8条，此处打7条，8条明杠了，用来做提示
                $count = count($news);
                $articles[$i]['Title'] = '还有' . $count. '条没有展示，请回复"mm"获取查看，5分钟内有效';

                //缓存过滤掉已回复后的数组内容,缓存标识为客户openId
                S($openid , array(
                    'action' => array(
                        'controller' => "Cache,Logic" , //需要后续处理的控制器及命名空间
                        'method' => 'newsCache' , //需要后续处理的公共方法
                    ) ,
                    'needs' => array(
                        'keywords' => 'mm,mM,MM,Mm' ,
                    ) ,
                    'news' => $news
                ) ,
                    300);

                $i++;//第8条提示后，发送数据条数需为8.
                break;
            }
        }

        $this->data['ArticleCount'] = $i;
        $this->data['Articles'] = $articles;
        unset( $i );
    }

    private function service() {
        $this->data['MsgType'] = "transfer_customer_service";
    }

    private function setService($service) {
        $this->data['MsgType'] = "transfer_customer_service";
        $this->data['service'] = $service;
    }

    /**
     * 数据XML编码
     *
     * @param  object $tpl  采用的XML模板
     * @param  mixed  $data 数据
     *
     * @return string
     */
    private function data2xml($type , $tpl , $data) {
        switch ( $type ) {
            case 'text':
                $resultStr = sprintf($tpl,$data['ToUserName'],$data['FromUserName'],$data['CreateTime'],$data['MsgType'],$data['ContentStr'],$data['MsgId']);
                break;
            case 'news':
                $articlesStr = "";
                foreach ( $data['Articles'] as $item ) {
                    $articlesStr .= "<item>
               <Title><![CDATA[" . $item['Title'] . "]]></Title>
               <Description><![CDATA[" . $item['Description'] . "]]></Description>
               <PicUrl><![CDATA[" . $item['PicUrl'] . "]]></PicUrl>
               <Url><![CDATA[" . $item['Url'] . "]]></Url>
               </item>";
                }
                $resultStr = sprintf($tpl , $data['ToUserName'] , $data['FromUserName'] , $data['CreateTime'] , $data['MsgType'] , $data['ArticleCount'] , $articlesStr);
                break;

            case 'music':
                $resultStr = sprintf();
                break;

            case 'service':
                // 没有指定客服的情况下自动转接到有空的客服
                $resultStr = sprintf($tpl , $data['ToUserName'] , $data['FromUserName'] , $data['CreateTime'] , $data['MsgType']);
                break;

            case 'setService':
                $resultStr = sprintf($tpl , $data['ToUserName'] , $data['FromUserName'] , $data['CreateTime'] , $data['MsgType'] , $data['service']);
                break;
            default:
                $resultStr ='';
        }

        return $resultStr; //最终的回复结果不能用return，只能用echo ，否则没响应。
    }


    /**
     * 验证方法

     */
    private function valid() {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if ( $this->checkSignature() ) {
            echo $echoStr;
            exit;
        }
    }

    /**
     * 验证签名方法
     */
    private function checkSignature() {

        $signature = $_GET["signature"];

        $timestamp = $_GET["timestamp"];

        $nonce = $_GET["nonce"];


        $token = $this->token;

        $tmpArr = array( $token , $timestamp , $nonce );

        sort($tmpArr , SORT_STRING);

        $tmpStr = implode($tmpArr);

        $tmpStr = sha1($tmpStr);


        if ( $tmpStr == $signature ) {

            return true;

        } else {

            return false;

        }

    }
}
